// RTSP Server
#include <bits/stdint-uintn.h>
#include <net/Socket.h>
#include <xop/RtspServer.h>
#include "net/Timer.h"
#include <thread>
#include <memory>
#include <iostream>
#include <string>

#include <linux/videodev2.h>
#include <camera.h>
#include <videoEncoder.h>

void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, Camera* h264_file);

int main(int argc, char **argv)
{

	Camera cam("/dev/video0",1920,1080,V4L2_PIX_FMT_NV12);
	cam.capture_image("/home/demo.yuv");

	std::string suffix = "live";
	std::string ip = "192.168.1.108";
	std::string port = "554";
	std::string rtsp_url = "rtsp://" + ip + ":" + port + "/" + suffix;
	
	std::shared_ptr<xop::EventLoop> event_loop(new xop::EventLoop());
	std::shared_ptr<xop::RtspServer> server = xop::RtspServer::Create(event_loop.get());

	if (!server->Start("0.0.0.0", atoi(port.c_str()))) {
		printf("RTSP Server listen on %s failed.\n", port.c_str());
		return 0;
	}

#ifdef AUTH_CONFIG
	server->SetAuthConfig("-_-", "admin", "12345");
#endif

	xop::MediaSession *session = xop::MediaSession::CreateNew("live"); 
	session->AddSource(xop::channel_0, xop::H264Source::CreateNew()); 
	//session->StartMulticast(); 
	session->AddNotifyConnectedCallback([] (xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port){
		printf("RTSP client connect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port);
	});
   
	session->AddNotifyDisconnectedCallback([](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) {
		printf("RTSP client disconnect, ip=%s, port=%hu \n", peer_ip.c_str(), peer_port);
	});

	xop::MediaSessionId session_id = server->AddSession(session);
         
	std::thread t1(SendFrameThread, server.get(), session_id, &cam);
	t1.detach(); 

	std::cout << "Play URL: " << rtsp_url << std::endl;

	while (1) {
		xop::Timer::Sleep(100);
	}

	getchar();
	return 0;
}

static int store_image(const void *data, int size)
{

        fflush(stderr);
        fprintf(stderr, ">");

        char file_name[64] = {0};
		FILE *fp = NULL;
        snprintf(file_name, sizeof(file_name),
             "%s", "/home/demo.yuv");
        fp = fopen(file_name, "ab+");
        if (fp == NULL) {
                printf("fopen yuv file %s failed!\n", file_name);
                return -1;
        }
        
        fwrite(data, size, 1, fp);
        printf("size %d \n", size);
        printf("Write success YUV data to %s \n",file_name);
        fflush(fp);

        return 0;
}

void SendFrameThread(xop::RtspServer* rtsp_server, xop::MediaSessionId session_id, Camera* cam)
{       
	int buf_size = 1024 * 1024 * 4;
	std::unique_ptr<uint8_t> frame_buf(new uint8_t[buf_size]);
	std::unique_ptr<uint8_t> encode_buf(new uint8_t[buf_size]);
	std::shared_ptr<VideoEncoder> encoder = VideoEncoder::Create(VIDEO_CODEC_X264);
	VideoEncoder::encSetting param = VideoEncoder::encSetting();
	size_t pkt_size;
	bool is_keyframe;
	bool got_output;

	param.width = cam->width;
	param.height = cam->height;
	param.bitrate_bps = 4000;
	param.fps = 30;
	encoder->Open(param);

	while(1) {
		int ret = 0;
		int frame_size = 0;
		ret = cam->read_frame((char*)frame_buf.get(), buf_size, frame_size);
		if (ret)
		{
			break;
		}
		
		if(frame_size > 0) {
			xop::AVFrame videoFrame = {0};
			
			if(!encoder->Encode((uint8_t *)frame_buf.get(), (uint8_t *)encode_buf.get(), pkt_size, is_keyframe, got_output))
			{	
				printf("encode fail\n");
				continue;
			}
			videoFrame.type = 0; 
			videoFrame.size = pkt_size;
			videoFrame.timestamp = xop::H264Source::GetTimestamp();
			videoFrame.buffer.reset(new uint8_t[videoFrame.size]);    
			memcpy(videoFrame.buffer.get(), encode_buf.get(), pkt_size);
			rtsp_server->PushFrame(session_id, xop::channel_0, videoFrame);
			
		}
		else {
			break;
		}
	};
	encoder->Close();
}




